import assert from 'node:assert/strict';

import type { PartialDeep } from 'type-fest';

import type { EggAppConfig, EggAppInfo, EggEnvType } from './types.ts';

/**
 * Partial EggAppConfig
 */
export type PartialEggConfig = PartialDeep<EggAppConfig>;

/**
 * Configuration factory function return type
 */
export type EggConfigFactory = (appInfo: EggAppInfo) => PartialEggConfig;

/**
 * Define configuration with type safety
 * @example
 * ```ts
 * import { defineConfig } from 'egg';
 *
 * export default defineConfig({
 *   keys: 'my-keys',
 *   middleware: []
 * });
 * ```
 */
export function defineConfig(config: PartialEggConfig): PartialEggConfig {
  return config;
}

/**
 * Define configuration factory function with type safety
 * @example
 * ```ts
 * export default defineConfigFactory((appInfo): PartialEggConfig => ({
 *   keys: appInfo.name + '_keys',
 *   middleware: []
 * }));
 * ```
 */
export function defineConfigFactory(configFactory: EggConfigFactory): EggConfigFactory {
  return configFactory;
}

/**
 * Define plugin meta with type safety
 */
export interface EggPluginMeta {
  /** the plugin name, it can be used in `dep` */
  name: string;
  /** whether enabled */
  enable: boolean;
  /** the directory of the plugin package */
  path: string;
  /** the dependent plugins, you can use the plugin name */
  dependencies?: string[];
  /** the optional dependent plugins. */
  optionalDependencies?: string[];
  /** specify the serverEnv that only enable the plugin in it, default to all envs */
  env?: EggEnvType[];
}

/**
 * Egg plugin options, user can't provide name, it will be generated by the plugin factory.
 */
export type EggPluginOptions = PartialDeep<Omit<EggPluginMeta, 'name'>>;

/**
 * Egg plugin factory type, the return value is a record of plugin name and plugin meta.
 */
export type EggPluginFactory = (options?: EggPluginOptions) => Record<string, EggPluginMeta>;

/**
 * Define plugin factory with type safety
 * @example
 * ```ts
 * import { definePluginFactory } from 'egg';
 *
 * export default definePluginFactory({
 *   name: 'my-plugin',
 *   enable: true,
 *   path: 'path/to/my-plugin',
 *   dependencies: ['watcher'],
 *   optionalDependencies: ['view'],
 *   env: ['local', 'unittest'],
 * });
 * ```
 */
export function definePluginFactory(pluginMeta: EggPluginMeta): EggPluginFactory {
  assert(pluginMeta.name, 'plugin name is required');
  assert(pluginMeta.path, 'plugin path is required');
  return (options?: EggPluginOptions) => ({
    [pluginMeta.name]: {
      ...pluginMeta,
      ...options,
      name: pluginMeta.name,
      // skip merge `eggPlugin` field from package.json
      skipMerge: true,
    },
  });
}
